/* eslint-disable no-var */
"use strict";

let commonjsGlobal =
  typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};

function createCommonjsModule(fn, module) {
  return (module = { exports: {} }), fn(module, module.exports), module.exports;
}

let promise = createCommonjsModule(function (module) {
  (function (root) {
    // Store setTimeout reference so promise-polyfill will be unaffected by
    // other code modifying setTimeout (like sinon.useFakeTimers())
    let setTimeoutFunc = setTimeout;

    function noop() {}

    // Polyfill for Function.prototype.bind
    function bind(fn, thisArg) {
      return function () {
        fn.apply(thisArg, arguments);
      };
    }

    function Promise(fn) {
      if (typeof this !== "object") throw new TypeError("Promises must be constructed via new");
      if (typeof fn !== "function") throw new TypeError("not a function");
      this._state = 0;
      this._handled = false;
      this._value = undefined;
      this._deferreds = [];

      doResolve(fn, this);
    }

    function handle(self, deferred) {
      while (self._state === 3) {
        self = self._value;
      }
      if (self._state === 0) {
        self._deferreds.push(deferred);
        return;
      }
      self._handled = true;
      Promise._immediateFn(function () {
        let cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
        if (cb === null) {
          (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
          return;
        }
        let ret;
        try {
          ret = cb(self._value);
        } catch (e) {
          reject(deferred.promise, e);
          return;
        }
        resolve(deferred.promise, ret);
      });
    }

    function resolve(self, newValue) {
      try {
        // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
        if (newValue === self) throw new TypeError("A promise cannot be resolved with itself.");
        if (newValue && (typeof newValue === "object" || typeof newValue === "function")) {
          let then = newValue.then;
          if (newValue instanceof Promise) {
            self._state = 3;
            self._value = newValue;
            finale(self);
            return;
          } else if (typeof then === "function") {
            doResolve(bind(then, newValue), self);
            return;
          }
        }
        self._state = 1;
        self._value = newValue;
        finale(self);
      } catch (e) {
        reject(self, e);
      }
    }

    function reject(self, newValue) {
      self._state = 2;
      self._value = newValue;
      finale(self);
    }

    function finale(self) {
      if (self._state === 2 && self._deferreds.length === 0) {
        Promise._immediateFn(function () {
          if (!self._handled) {
            Promise._unhandledRejectionFn(self._value);
          }
        });
      }

      for (let i = 0, len = self._deferreds.length; i < len; i++) {
        handle(self, self._deferreds[i]);
      }
      self._deferreds = null;
    }

    function Handler(onFulfilled, onRejected, promise) {
      this.onFulfilled = typeof onFulfilled === "function" ? onFulfilled : null;
      this.onRejected = typeof onRejected === "function" ? onRejected : null;
      this.promise = promise;
    }

    /**
     * Take a potentially misbehaving resolver function and make sure
     * onFulfilled and onRejected are only called once.
     *
     * Makes no guarantees about asynchrony.
     */
    function doResolve(fn, self) {
      let done = false;
      try {
        fn(
          function (value) {
            if (done) return;
            done = true;
            resolve(self, value);
          },
          function (reason) {
            if (done) return;
            done = true;
            reject(self, reason);
          }
        );
      } catch (ex) {
        if (done) return;
        done = true;
        reject(self, ex);
      }
    }

    Promise.prototype["catch"] = function (onRejected) {
      return this.then(null, onRejected);
    };

    Promise.prototype.then = function (onFulfilled, onRejected) {
      let prom = new this.constructor(noop);

      handle(this, new Handler(onFulfilled, onRejected, prom));
      return prom;
    };

    Promise.all = function (arr) {
      let args = Array.prototype.slice.call(arr);

      return new Promise(function (resolve, reject) {
        if (args.length === 0) return resolve([]);
        let remaining = args.length;

        function res(i, val) {
          try {
            if (val && (typeof val === "object" || typeof val === "function")) {
              let then = val.then;
              if (typeof then === "function") {
                then.call(
                  val,
                  function (val) {
                    res(i, val);
                  },
                  reject
                );
                return;
              }
            }
            args[i] = val;
            if (--remaining === 0) {
              resolve(args);
            }
          } catch (ex) {
            reject(ex);
          }
        }

        for (let i = 0; i < args.length; i++) {
          res(i, args[i]);
        }
      });
    };

    Promise.resolve = function (value) {
      if (value && typeof value === "object" && value.constructor === Promise) {
        return value;
      }

      return new Promise(function (resolve) {
        resolve(value);
      });
    };

    Promise.reject = function (value) {
      return new Promise(function (resolve, reject) {
        reject(value);
      });
    };

    Promise.race = function (values) {
      return new Promise(function (resolve, reject) {
        for (let i = 0, len = values.length; i < len; i++) {
          values[i].then(resolve, reject);
        }
      });
    };

    // Use polyfill for setImmediate for performance gains
    Promise._immediateFn =
      (typeof setImmediate === "function" &&
        function (fn) {
          setImmediate(fn);
        }) ||
      function (fn) {
        setTimeoutFunc(fn, 0);
      };

    Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
      if (typeof console !== "undefined" && console) {
        console.warn("Possible Unhandled Promise Rejection:", err); // eslint-disable-line no-console
      }
    };

    /**
     * Set the immediate function to execute callbacks
     * @param fn {function} Function to execute
     * @deprecated
     */
    Promise._setImmediateFn = function _setImmediateFn(fn) {
      Promise._immediateFn = fn;
    };

    /**
     * Change the function to execute on unhandled rejection
     * @param {function} fn Function to execute on unhandled rejection
     * @deprecated
     */
    Promise._setUnhandledRejectionFn = function _setUnhandledRejectionFn(fn) {
      Promise._unhandledRejectionFn = fn;
    };

    if ("object" !== "undefined" && module.exports) {
      module.exports = Promise;
    } else if (!root.Promise) {
      root.Promise = Promise;
    }
  })(commonjsGlobal);
});

/**
 * Analyze client useragent
 *   1. detect client type
 *   2. gather necessary information
 */

function ua() {
  let APP = ["eleme", "napos"];
  let OS = ["windows", "android", "iphone_os", "ios"];
  let REGX_ID = /^[a-f0-9]{8}-[a-f0-9]{4}-[34][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/;
  let REGX_VER = /^(\d+)(?:\.(\d+)|)(?:\.(\d+)|)(?:-(alpha|beta|rc)(\d*)|)(?:-(dev)|)$/;
  let UA = navigator.userAgent.toLowerCase().replace(/\s+/g, " ").split(";")[0].split(" ");

  let info = { isClient: false };

  let uaCore = UA.map(function (str) {
    let sp = str.indexOf("/");
    return [str.substr(0, sp), str.substr(sp + 1)];
  });
  if (uaCore.length < 5) {
    info.uaNotRecognized = "INFO_SHORTAGE";
    return info;
  }

  let hasRomInfo = uaCore.length >= 6;
  if (hasRomInfo) {
    while (uaCore.length > 6) {
      uaCore.splice(4, 1);
    }
  }

  let ref = uaCore.pop();
  let key = ref[0];
  let appId = ref[1];
  if (key !== "id" || !REGX_ID.test(appId)) {
    info.uaNotRecognized = "ILLEGAL_APPID";
    return info;
  }
  info.id = appId;

  let ref$1 = uaCore.pop();
  let appName = ref$1[0];
  let appVersion = ref$1[1];
  if (APP.indexOf(appName) === -1 || !REGX_VER.test(appVersion)) {
    info.uaNotRecognized = "UNKNOWN_APP";
    return info;
  }
  info.app = { name: appName, version: appVersion };

  info.display = hasRomInfo ? uaCore.pop()[1] : null;

  let ref$2 = uaCore.pop();
  let osName = ref$2[0];
  let osVersion = ref$2[1];
  if (OS.indexOf(osName) === -1) {
    info.uaNotRecognized = "UNKNOWN_OS";
    return info;
  }
  info.os = {
    name: osName === "iphone_os" ? "ios" : osName,
    version: osVersion
  };

  let ref$3 = uaCore.pop();
  let deviceName = ref$3[0];
  let deviceModel = ref$3[1];
  info.device = {
    name: deviceName,
    model: deviceName === "pc" ? "" : deviceModel
  };

  info.isClient = true;
  return info;
}

let ua$1 = ua();

function error(condition, message) {
  if (!condition) {
    typeof console !== "undefined" && console.error("[eleme-openApi-jssdk] " + message);
  }
}

function isFunction(fn) {
  return typeof fn === "function";
}

function isObject(obj) {
  return typeof obj === "object";
}

function generateUTID(oneDirection) {
  if (oneDirection === void 0) oneDirection = false;

  let UTID_CHAR_ARRAY = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".split("");
  let UTID_CHAR_COUNT = UTID_CHAR_ARRAY.length;
  let UTID_LENGTH = 16;
  let str = "";
  if (!oneDirection) {
    for (let i = 0; i < UTID_LENGTH; i++) {
      let index = Math.floor(Math.random() * UTID_CHAR_COUNT);
      str += UTID_CHAR_ARRAY[index];
    }
  }
  return str;
}

function isNeedleView() {
  return navigator.userAgent.indexOf("NEEDLE") !== -1 || navigator.userAgent.indexOf("Needle") !== -1;
}

let FakeNativeApi = function FakeNativeApi() {};

FakeNativeApi.prototype.requestToClient = function requestToClient(requestJson) {
  let request = JSON.parse(requestJson);
  let fnName = request.functionName;
  let args = request["arguments"];
  let utid = request.utid;
  let error = null;
  let results = [];

  switch (fnName) {
    case "browser.dismiss":
      window.history.back();
      break;

    case "get":
      switch (args[0]) {
        case "runtime.topbar.title":
          results = [window.document.title];
          break;

        case "runtime.browser.isAppInstalled":
          results = [true];
          break;

        case "runtime.auth.ksid":
        case "runtime.restaurant.restaurantId":
          error = { code: -1, message: "Unsupported method" };
          break;
      }
      break;

    case "set":
      switch (args[0]) {
        case "runtime.topbar.title":
          if (typeof args[1] != "string") {
            error = { code: -2, message: "Invalid argument, #1 should be string" };
          } else {
            window.document.title = args[1];
            results = [true];
          }
          break;
      }
      break;

    default:
      error = { code: -1, message: 'Invalid function name "' + fnName + '"' };
      break;
  }

  setTimeout(function () {
    window.__napos__respondToBrowser__({ utid: utid, error: error, results: results });
  }, 200);
};

FakeNativeApi.prototype.respondToClient = function respondToClient() {};

let requestToClient = function () {};
let respondToClient = function () {};
if (!isNeedleView()) {
  if (!ua$1.isClient) {
    let fakeApi = new FakeNativeApi();
    requestToClient = fakeApi.requestToClient.bind(fakeApi);
    respondToClient = fakeApi.respondToClient.bind(fakeApi);
  } else if (ua$1.os.name === "android") {
    if (isObject(window.__napos__)) {
      if (isFunction(window.__napos__.requestToClient)) {
        requestToClient = window.__napos__.requestToClient.bind(window.__napos__);
      } else {
        error(false, "cannot detect window.__napos__.requestToClient function");
      }

      if (isFunction(window.__napos__.respondToClient)) {
        respondToClient = window.__napos__.respondToClient.bind(window.__napos__);
      } else {
        error(false, "cannot detect window.__napos__.respondToClient function");
      }
    } else {
      error(false, "cannot detect window.__napos__ object");
    }
  } else if (ua$1.os.name === "ios") {
    let loadUrl = function (scheme, url) {
      let ifr = document.createElement("iframe");
      ifr.setAttribute("src", scheme + "://" + encodeURIComponent(url));
      ifr.setAttribute("style", "display: none");
      ifr.setAttribute("height", "0px");
      ifr.setAttribute("width", "0px");
      ifr.setAttribute("frameborder", "0");
      document.body.appendChild(ifr);
      ifr.parentNode.removeChild(ifr);
      ifr = null;
    };
    requestToClient = loadUrl.bind(null, "napos-request-to-client");
  }
}

// Native -> Web (Native request, Web response)
let handlers = {};

function doRespondToClient(utid, error, results) {
  respondToClient(JSON.stringify({ utid: utid, error: error, results: results }));
}

if (ua$1.os && ua$1.os.name !== "windows") {
  window.__napos__requestToBrowser__ = function (request) {
    let functionName = request[0];
    let utid = request[1];
    let handler = handlers[functionName];
    if (isFunction(handler)) {
      let args = request["arguments"];
      if (utid) {
        args.push(doRespondToClient.bind(null, utid));
      }
      handler.apply(void 0, args);
    }
  };
}

// Web -> Native (Web request, Native response)
let callbacks = {};

function doRequestToClient(oneDirection, functionName, args, cb) {
  if (args === void 0) args = [];

  let _utid = generateUTID(oneDirection);
  if (!oneDirection && isFunction(cb)) {
    callbacks[_utid] = cb;
  }
  requestToClient(
    JSON.stringify({
      utid: _utid,
      functionName: functionName,
      arguments: args
    })
  );
}

if (ua$1.os && ua$1.os.name !== "windows") {
  window.__napos__respondToBrowser__ = function (respond) {
    let utid = respond.utid;
    let results = respond.results;
    let error = respond.error;
    let cb = callbacks[utid];
    if (isFunction(cb)) {
      let _r = results || [];
      cb.apply(void 0, [error].concat(_r));
    }
  };
}

let lib = createCommonjsModule(function (module, exports) {
  (function (root, factory) {
    if (typeof undefined === "function" && undefined.amd) {
      undefined([], factory);
    } else if ("object" === "object") {
      module.exports = factory();
    } else {
    }
  })(commonjsGlobal, function () {
    if (typeof weex === "object") {
      window = {
        navigator: {
          userAgent: weex.config.env.platform + " Needle/Weex"
        }
      };
    }

    /**
     * @namespace needle
     * @version 0.3.0
     * @global
     * @author hongju.wang@ele.me, jianxiang.jin@ele.me
     */
    let callQueue = [];
    let eventMap = {};
    let stashedEvents = [];

    /**
     * 判断是否是Android平台
     * @return {Boolean}
     * @function
     * @memberof needle
     * @return {Boolean}
     */
    function isAndroid() {
      let ua = window.navigator.userAgent.toLowerCase();
      return ua.indexOf("android") > -1;
    }

    /**
     * 判断是否是在 Needle 的WebView 环境
     * @return {Boolean}
     * @function
     * @memberof needle
     * @return {Boolean}
     */
    function isNeedle() {
      let ua = window.navigator.userAgent.toLowerCase();
      return ua.indexOf("needle") > -1;
    }

    /**
     * 判断是否是iOS平台
     * @return {Boolean}
     * @function
     * @memberof needle
     * @return {Boolean}
     */
    function isIOS() {
      let ua = window.navigator.userAgent.toLowerCase();
      return ua.indexOf("iphone") > -1;
    }

    /**
     * 判断是否是 Weex 环境
     * @return {Boolean}
     * @function
     * @memberof needle
     * @return {Boolean}
     */
    function isWeex() {
      return typeof weex === "object";
    }

    if (isWeex()) {
      let globalEvent = weex.requireModule("globalEvent");
      globalEvent.addEventListener("_needleEvent", function (event) {
        let eventName = event.eventName;
        let eventData = event.data;
        if (typeof event.data === "object") {
          eventData = event.data;
        } else {
          try {
            eventData = JSON.parse(event.data);
          } catch (e) {}
        }
        let callback = eventAndCallbackMap[eventName];
        setTimeout(function () {
          !!callback && callback(eventData);
        }, 0);
      });

      globalEvent.addEventListener("_needleCall", function (event) {
        let response = event.data;
        if (typeof event.data === "object") {
          response = event.data;
        } else {
          try {
            response = JSON.parse(event.data);
          } catch (e) {}
        }
        let callback = callingAndCallbackMap[response.id];
        response = response.result;
        setTimeout(function () {
          if (!!callback && !!response) {
            if (!!response.err) {
              callback(response.err, null);
            } else {
              callback(null, response.res);
            }
          }
          delete callingAndCallbackMap[response.id];
        }, 0);
      });
    }

    // For Android only
    let uniqueId = 0; //请求调用的 message ID
    var callingAndCallbackMap = {}; //调用以及回调的对应关系
    var eventAndCallbackMap = {}; //事件名字以及事件回调的对应关系
    if (!isWeex()) {
      window.__needleBrowserTunnel = {
        /**
         * read data from Android
         * {id, params, result}
         */
        read: function (responseString) {
          let response = JSON.parse(responseString);
          let callback = callingAndCallbackMap[response.id];
          response = response.result;
          setTimeout(function () {
            if (!!callback && !!response) {
              if (!!response.err) {
                callback(response.err, null);
              } else {
                callback(null, response.res);
              }
            }
            delete callingAndCallbackMap[response.id];
          }, 0);
        },
        on: function (eventName, data) {
          /**
           * receive event and event data from Android
           */
          let eventData = data;
          try {
            eventData = JSON.parse(data);
          } catch (e) {}
          let callback = eventAndCallbackMap[eventName];
          setTimeout(function () {
            !!callback && callback(eventData);
          }, 0);
        }
      };
    }

    /**
     * 根据插件名字调用(简称为名字调用)
     * @function
     * @memberof needle
     * @param {String} name 插件名字(自定义插件名字需要与客户端开发一起协商, 内置插件名字均已 needle 开头，业务自定义插件可添加自定义前缀避免命名冲突)
     * @param {Object} params 参数
     * @param {function} callback 回调函数, 函数签名callback(err, res),第一个总是 err, 第二个参数是正常 response（与 Node.js 回调类似）
     */
    function execute(name, params, callback) {
      if (isWeex()) {
        var id = "msg_" + uniqueId++ + "_" + new Date().getTime();
        callingAndCallbackMap[id] = callback;
        weex.requireModule("NeedleNativeModule").execute({ id: id, name: name, params: params });
        return;
      }

      if (isAndroid() && !!window.__needleClientTunnel) {
        var id = "msg_" + uniqueId++ + "_" + new Date().getTime();
        callingAndCallbackMap[id] = callback;
        window.__needleClientTunnel.read(JSON.stringify({ id: id, name: name, params: params }));
      } else {
        if (!!window.WebViewJavascriptBridge) {
          _doCall(name, params, callback);
        } else {
          callQueue.push({ name: name, params: params, callback: callback });
        }
      }
    }

    /**
     * 判断是否 ready
     * @function
     * @memberof needle
     * @return {Boolean} true 表示 needle 初始化完毕
     */
    function isReady() {
      return !!window.WebViewJavascriptBridge || !!window.__needleClientTunnel || isWeex();
    }

    function _doCall(name, params, callback) {
      !!window.WebViewJavascriptBridge &&
        window.WebViewJavascriptBridge.callHandler(name, params, function (res) {
          let response = isAndroid() ? JSON.parse(res) : res;
          if (!!callback && !!response) {
            if (!!response.err) {
              callback(response.err, null);
            } else {
              callback(null, response.res);
              if (window.__needle__debug) {
                console.log("Plugin name:", name, ", Params:", params, ", Response:", response);
              }
            }
          }
        });
    }

    /**
     * 注册事件监听
     * @function
     * @memberof needle
     * @param {String} eventName  事件名字
     * @param {function} callback 事件发生时的回调
     */
    function on(eventName, callback) {
      if (isWeex()) {
        eventAndCallbackMap[eventName] = callback;
        return;
      }

      if (isAndroid() && !!window.__needleClientTunnel) {
        eventAndCallbackMap[eventName] = callback;
        return;
      }

      eventMap[eventName] = { eventName: eventName, callback: callback };
      if (!!window.WebViewJavascriptBridge) {
        WebViewJavascriptBridge.registerHandler(eventName, function (data) {
          _doFlushEventListeners(eventName, data);
        });
      } else {
        stashedEvents.push({ eventName: eventName, callback: callback });
      }
    }

    function _doFlushEventListeners(eventName, data) {
      let event = eventMap[eventName];
      if (!!event) {
        event.callback(data);
      }
    }

    /**
     * 设备相关的 API
     *@namespace needle.app
     *@memberof needle
     */
    let app = {
      /**
     * 获取设备信息
     * @example
     ## 返回值
     {
       appId : "me.ele.needle",
       appVersion : "1.0",
       buildNumber : 1,
       deviceId : "2146F58E-97A2-4D94-A18A-3C72B6C68BFB",
       platform : enum('Android', 'iOS')
       resolution : 640x1136,
       systemVersion : "10.1",
       networkType : enum('2G', '3G', '4G', 'WIFI', 'UNKNOW'),
       userAgent : "Mozilla/5.0 (iP......
       sdkVersion: '0.1.12'
      }
     * @function
     * @memberof needle.app
     * @param {function} callback 回调函数
     */
      getEnvInfo: function (callback) {
        execute("needle.app.env", {}, callback);
      },

      /**
       * 探测宿主手机某 APP 是否安装
       * @function
       * @memberof needle.app
       * @param {Object} param - {package: String, schemeUrl: String}, Android id 为 package，iOS 为 schemeUrl
       * @param {function} callback 回调函数 - res: {isAppInstalled: Boolean}
       */
      isAppInstalled: function (param, callback) {
        execute("needle.app.installation", param, callback);
      },

      /**
     * 编辑短信到发送短信页面等待用户确认发送
     * @example
     ##调用
     var option = {
            tel: 13857702077,
            message: 'this is a needle sms message.'
        };
     needle.app.sendSMS(option,function(err, res){
        if(!err){
           ;
        }
      });
     * @function
     * @memberof needle.app
     * @param {Object} param - {tel: String, message: String}
     * @param {function} callback 回调函数
     */
      sendSMS: function (param, callback) {
        execute("needle.app.sms", param, callback);
      },

      /**
     * 获取定位信息
     * @example
     * needle.app.getLocation(function(err, res){
     *   if(!err){
     *      res = {
                "longitude":89.33 ,
                "latitude": 101.11
            }
     *   }
     * });
     * @example
     ##成功响应
     {
       "longitude":89.33 ,
       "latitude": 101.11
     }
     * @function
     * @memberof needle.app
     * @param {function} callback 回调函数
     */
      getLocation: function (callback) {
        execute("needle.app.location", {}, callback);
      },

      /**
       * 在新的 WebView 实例中打开 Uri
       * @function
       * @example
       * //参数
       * {
       *   url: "http://www.google.com"
       *   external: true
       * }
       * @memberof needle.app
       * @param {Object} param {url: String, external: Boolean}, external 是否使用系统浏览器打开
       * @param {function} callback 回调函数
       */
      openUri: function (param, callback) {
        execute("needle.app.uri", param, callback);
      },

      /**
       * 清除webview缓存
       * Added: Android: 0.1.2
       * Added: iOS: 0.1.0
       * @function
       * @memberof needle.app
       * @param {function} callback 回调函数
       */
      clearWebsiteCache: function (callback) {
        execute("needle.app.web.cache.clear", {}, callback);
      }
    };

    /**
     * WebView 以及所在容器的一些相关操作
     *@namespace needle.container
     *@memberof needle
     */
    let container = {
      /**
     * 显示 Toast
     * @example
     ## 调用
     {
       message: msg,
       duration: 1000,
       offset: 0
     }
     * @function
     * @memberof needle.container
     */
      toast: function (param) {
        execute("needle.container.toast", param, null);
      },

      /**
       * 关闭当前页面（Native 页面）
       * @function
       * @memberof needle.container
       */
      close: function () {
        execute("needle.container.window", { type: "close" }, null);
      },

      /**
     * reload 网页，可以指定 url
     * @function
     * @example
     ## param 示例
     {
       url: 'http://www.google.com'
     }
     * @memberof needle.container
     * @param {function} callback 回调函数
     */
      reload: function (param, callback) {
        var param = param || {};
        param.type = "reload";
        execute("needle.container.window", param, callback);
      },

      /**
       * 当前网页回退
       * @function
       * @memberof needle.container
       * @param {function} callback 回调函数
       */
      goBack: function (callback) {
        execute("needle.container.window", { type: "back" }, callback);
      },

      /**
       * 显示 loading
       * @function
       * @memberof needle.container
       */
      showLoading: function () {
        execute("needle.container.loading", { type: "show" }, null);
      },

      /**
       * 隐藏 loading
       * @function
       * @memberof needle.container
       */
      hideLoading: function () {
        execute("needle.container.loading", { type: "hide" }, null);
      },

      /**
     * 设置导航栏的标题
     * @function
     * @example
     ## param 示例
     {
       title: ‘导航栏标题'
     }
     * @memberof needle.container
     * @param param 见示例
     * @param {function} callback 回调函数
     */
      setTitle: function (param, callback) {
        var param = param || {};
        param.type = "set";
        execute("needle.container.title", param, null);
      },

      /**
     * 获取导航栏的标题
     * @function
     * @example
     ## callback res
     "{
       title: '获取到的标题'
     "}
     * @memberof needle.container
     * @param {function} callback 回调函数
     */
      getTitle: function (callback) {
        execute("needle.container.title", { type: "get" }, callback);
      },

      /**
       * 显示原生导航栏
       * @function
       * @memberof needle.container
       * @param {function} callback 回调函数
       */
      showTitleBar: function (callback) {
        execute("needle.container.titleBar", { type: "show" }, callback);
      },

      /**
       * 隐藏原生导航栏
       * @function
       * @memberof needle.container
       * @param {function} callback 回调函数
       */
      hideTitleBar: function (callback) {
        execute("needle.container.titleBar", { type: "hide" }, callback);
      },

      /**
     * 设置原生导航栏右上角 menu 的icon
     * @function
     * @example
     ## param 示例
     {
       icon: 'http://icon_url' //
     }
     @example
     ## 调用示例
     needle.container.registerMenu({ icon: 'http://avatar.csdn.net/7/A/A/1_qq_19711823.jpg'}, function () {
            toast("Menu clicked:");
     });
     * @memberof needle.container
     * @param {Object} param {icon: String}
     * @param {function} onclick menu 点击会触发此 callback
     */
      registerMenu: function (param, onclick) {
        execute("needle.container.menu", param, null);
        on("needle.container.menu.onclick", onclick);
      },

      /**
     * 设置底部弹出 menu 的 icon，文字, key
     * @function
     * @example
     ## param 示例
     menus: [{
                        icon: '',
                        text: '',
                        key: '' // 唯一标识
                    }]
     ## callback res 示例
     {
       icon: '',
       text: '',
       key: ''
     }
     * @memberof needle.container
     * @param {Object} param - 见示例
     * @param {function} callback 回调函数
     */
      registerBottomMenu: function (param, callback) {
        execute("needle.container.bottomMenu", param, null);
        on("needle.container.bottomMenu.onclick", callback);
      }
    };

    /**
     * 图片选择以及图片预览相关 API
     *@namespace needle.image
     *@memberof needle
     */
    let image = {
      /**
     * 选择图片
     * @example
     ## param 示例
     {
      type: enum('camera', 'album'),
      options: {
        maxSize: Number, // b, 比如 1024*50 = 50 Kb
        width: Number,
        height:Number
      }
     }
     @example
     ## callback res 示例
     {
       base64: 'base64 encode'
     }
     * @function
     * @memberof needle.image
     * @param {Object} param 见示例
     * @param {function} callback 回调函数
     */
      pick: function (param, callback) {
        execute("needle.image.picker", param, callback);
      },

      /**
     * 预览图片
     * @function
     @example
     ## param 示例
     { images :[{uri: 'uri'}] }
     * @memberof needle.image
     * @param {Object} param { images :[{uri: 'url'}] }
     * @param {function} callback 回调函数
     */
      preview: function (param, callback) {
        execute("needle.image.previewer", param, callback);
      },

      /**
     * 保存图片到系统相册
     * Added: Android 0.1.5
     * Added: iOS ?
     * @function
     @example
     ## param 示例
     { base64: 'base64 string'}
     * @memberof needle.image
     * @param {Object} param { base64: String}
     * @param {function} callback 回调函数
     */
      save: function (param, callback) {
        execute("needle.image.save", param, callback);
      }
    };

    /**
     * 可多 WebView 共享的 K-V 存储 API）
     * 注：不同 HOST 之间无法共享 Cache。例如 needle.ele.me 和 needle.elenet.me 无法共享 Cache。
     *@namespace needle.cache
     *@memberof needle
     */
    let cache = {
      /**
     * 设置 K-V, 相同的 key 会被覆盖
     * @function
     * @example
     ## param 示例
     {key: 'key', value: 'value'}
     * @memberof needle.cache
     * @param {Object} param {key: String, value: String}
     * @param {function} callback 回调函数
     */
      set: function (param, callback) {
        var param = param || {};
        param.method = "set";
        execute("needle.cache", param, callback);
      },

      /**
     * 获取 key 对应值
     * @function
     * @example
     ## param 示例
     {key: 'key'}
     * @memberof needle.cache
     * @param {Object} param {key: String}
     * @param {function} callback 回调函数
     */
      get: function (param, callback) {
        var param = param || {};
        param.method = "get";
        execute("needle.cache", param, callback);
      },

      /**
     * delete key 对应值
     * Added: Android 0.1.3
     * Added: iOS: 0.1.0
     * @function
     * @example
     ## param 示例
     {key: 'key'}
     * @memberof needle.cache
     * @param {Object} param {key: String}
     * @param {function} callback 回调函数
     */
      delete: function (param, callback) {
        var param = param || {};
        param.method = "delete";
        execute("needle.cache", param, callback);
      },

      /**
       * 删除所有 key 对应的值
       * Added: Android 0.1.3
       * Added: iOS: 0.1.0
       * @function
       * @memberof needle.cache
       * @param {function} callback 回调函数
       */
      deleteAll: function (callback) {
        var param = param || {};
        param.method = "clear";
        execute("needle.cache", param, callback);
      }
    };

    if (!isWeex()) {
      if (window.WebViewJavascriptBridge) {
        initJS();
      } else {
        // WebViewJavascriptBridgeReady 事件由宿主 WebView 注入的一段 JavaScript 触发
        document.addEventListener("WebViewJavascriptBridgeReady", initJS, false);
      }
    }

    function _flushQueue() {
      callQueue.forEach(function (item) {
        _doCall(item.name, item.params, item.callback);
      });
      callQueue = [];
    }

    function _flushEventListener() {
      stashedEvents.forEach(function (event) {
        !!WebViewJavascriptBridge &&
          WebViewJavascriptBridge.registerHandler(event.eventName, function (data) {
            _doFlushEventListeners(event.eventName, data);
          });
      });
      stashedEvents = [];
    }

    function initJS() {
      _flushEventListener();
      _flushQueue();
      let needleReadyEvent = document.createEvent("Events");
      needleReadyEvent.initEvent("needleReady");
      document.dispatchEvent(needleReadyEvent);
    }

    return {
      version: "0.3.0",
      isAndroid: isAndroid,
      isIOS: isIOS,
      isNeedle: isNeedle,
      isWeex: isWeex,
      isReady: isReady,
      execute: execute,
      on: on,
      app: app,
      container: container,
      image: image,
      cache: cache
    };
  });
});

function versionLowerThen730(version) {
  if (!version || typeof version !== "string") {
    return true;
  }
  // 默认版本规则不会超过10,同时保证有两位小数
  return parseInt(version.split(".").join("")) < 730;
}
let Mobile = function Mobile() {
  this.platform = "mobile";
  this.isNeddle = isNeedleView();
  this.callbacks = [];
};

Mobile.prototype._get = function _get(key, cb) {
  doRequestToClient(false, "get", [key], cb);
};

Mobile.prototype._set = function _set(key, val, cb) {
  doRequestToClient(false, "set", [key, val], cb);
};

Mobile.prototype._setUserId = function _setUserId(val, cb) {
  // eslint-disable-next-line @typescript-eslint/no-this-alias
  let this$1 = this;

  return new Promise(function (resolve, reject) {
    if (!this$1.isNeddle) {
      this$1._set("runtime.browser.userId", val, function (error, userId) {
        error ? reject(error) : resolve(userId);
      });
    } else {
      this$1.execute("napos.setUserId", val, function (err, res) {
        if (err) {
          reject(err);
        } else {
          resolve(res);
        }
      });
    }
  });
};

Mobile.prototype.execute = function execute(name, params, cb) {
  lib.execute(name, params, cb);
};

// functions expose to third parts
Mobile.prototype.dismiss = function dismiss() {
  doRequestToClient(true, "browser.dismiss");
};

Mobile.prototype.goForwardWithBrowser = function goForwardWithBrowser(url) {
  doRequestToClient(true, "browser.goForwardWithBrowser", [url]);
};

Mobile.prototype.openNaposFileChooser = function openNaposFileChooser(cb) {
  doRequestToClient(false, "browser.openNaposFileChooser", [], cb);
};

Mobile.prototype.isAppInstalled = function isAppInstalled(packageId, cb) {
  doRequestToClient(false, "get", ["runtime.browser.isAppInstalled", packageId], cb);
};

Mobile.prototype.printOrder = function printOrder(orderJsonString) {
  doRequestToClient(true, "browser.printOrder", [orderJsonString]);
};

Mobile.prototype.picture = function picture(uploadUrl, cb) {
  doRequestToClient(false, "browser.picture", [uploadUrl], cb);
};

Mobile.prototype.hideActionBar = function hideActionBar() {
  doRequestToClient(true, "browser.hideActionBar");
};

Mobile.prototype.showActionBar = function showActionBar() {
  doRequestToClient(true, "browser.showActionBar");
};

Mobile.prototype.showToast = function showToast(content) {
  doRequestToClient(true, "browser.showToast", [content]);
};

Mobile.prototype.hideLoading = function hideLoading() {
  doRequestToClient(true, "browser.hideLoading");
};

Mobile.prototype.showLoading = function showLoading() {
  doRequestToClient(true, "browser.showLoading");
};

Mobile.prototype.getUserId = function getUserId() {
  // eslint-disable-next-line @typescript-eslint/no-this-alias
  let this$1 = this;

  return new Promise(function (resolve, reject) {
    if (!this$1.isNeddle) {
      this$1._get("runtime.browser.userId", function (error, userId) {
        error ? reject(error) : resolve(userId);
      });
    } else {
      this$1.execute("napos.getUserId", {}, function (err, res) {
        if (err) {
          reject(err);
        } else {
          resolve(res);
        }
      });
    }
  });
};

Mobile.prototype.getCurrentShopId = function getCurrentShopId(cb) {
  // eslint-disable-next-line @typescript-eslint/no-this-alias
  let this$1 = this;

  function resolveSync() {
    return window.__napos__ && window.__napos__.getNaposRSTid ? window.__napos__.getNaposRSTid() : null;
  }
  if (!this.isNeddle) {
    return new Promise(function (resolve, reject) {
      this$1._get("runtime.restaurant.restaurantId", function (error, shopId) {
        error ? resolve(resolveSync()) : resolve(shopId);
      });
    });
  } else {
    return new Promise(function (resolve, reject) {
      this$1.execute("napos.getShopId", {}, function (err, res) {
        if (err) {
          reject(err);
        } else {
          resolve(res);
        }
      });
    });
  }
};

Mobile.prototype.setTitle = function setTitle(title) {
  if (!this.isNeddle) {
    this._set("runtime.topbar.title", title);
  } else {
    lib.container.setTitle({ title: title });
  }
};

Mobile.prototype.nativeAuth = function nativeAuth(config) {
  // eslint-disable-next-line @typescript-eslint/no-this-alias
  let this$1 = this;

  let appinfo = ua$1;
  let version = appinfo && appinfo.app && appinfo.app.version;
  if (versionLowerThen730(version)) {
    return Promise.reject("该版本不支持native授权,请使用web页面授权");
  }
  let keys = ["clientId", "state", "redirectUrl", "scope"];
  keys.forEach(function (key) {
    if (!config[key]) {
      throw Error(key + "can not be empty");
    }
  });
  if (typeof config.isProduction !== "boolean") {
    throw Error("isProduction must be boolean");
  }
  return new Promise(function (resolve, reject) {
    this$1.execute("napos.native.auth", config, function (err, res) {
      if (err) {
        reject(err);
      } else {
        resolve(res);
      }
    });
  });
};

// iframe -> melody (message: { utid, type, method, params })
let callbacks$1 = {};
function requestToContainer(method, params) {
  if (params === void 0) params = [];

  let _utid = generateUTID();
  return new Promise(function (resolve, reject) {
    if (window.parent === window) {
      reject("run environment is not iframe!");
    } else {
      window.top.postMessage(
        {
          utid: _utid,
          type: "jssdk-request",
          method: method,
          params: params
        },
        "*"
      );
      callbacks$1[_utid] = { resolve: resolve, reject: reject };
    }
  });
}

if (!ua$1.isClient || (ua$1.os && ua$1.os.name === "windows")) {
  window.addEventListener(
    "message",
    function (event) {
      let ref = event.data;
      let utid = ref.utid;
      let type = ref.type;
      let result = ref.result;
      let error = ref.error;
      if (type !== "jssdk-response") {
        return;
      }
      if (callbacks$1[utid]) {
        let ref$1 = callbacks$1[utid];
        let resolve = ref$1.resolve;
        let reject = ref$1.reject;
        if (error) {
          reject(error);
        } else {
          resolve(result);
        }
      }
    },
    true
  );
}

let Pc = function Pc() {
  this.platform = "pc";
};

Pc.prototype._setAppId = function _setAppId(appId) {
  return requestToContainer("setAppId", appId);
};

// functions expose to third parts
Pc.prototype.getContainerLocation = function getContainerLocation() {
  return requestToContainer("getLocation");
};

Pc.prototype.getUserId = function getUserId() {
  return requestToContainer("getUserId");
};

Pc.prototype.getCurrentShopId = function getCurrentShopId() {
  return requestToContainer("getCurrentShopId");
};

Pc.prototype.getChainRestaurantId = function getChainRestaurantId() {
  return requestToContainer("getChainRestaurantId");
};

Pc.prototype.getCodeForIdentity = function getCodeForIdentity(params) {
  let keys = ["appKey"];
  keys.forEach(function (key) {
    if (!params[key]) {
      throw Error(key + "can not be empty");
    }
  });
  if (typeof params.isProduction === "undefined") {
    params.isProduction = true;
  }
  return requestToContainer("getCodeForIdentity", params);
};

if (!window.Promise) {
  window.Promise = promise;
}
let Client = ua$1.isClient && ua$1.os && ua$1.os.name !== "windows" ? Mobile : Pc;

let Main = (function (Client) {
  function Main() {
    Client.call(this);
    this.userAgent = ua$1;
  }

  if (Client) Main.__proto__ = Client;
  Main.prototype = Object.create(Client && Client.prototype);
  Main.prototype.constructor = Main;

  Main.prototype.getUserAgent = function getUserAgent() {
    return this.userAgent;
  };

  Main.prototype.hasMethod = function hasMethod(methodName) {
    return !!this[methodName];
  };

  return Main;
})(Client);

let main = new Main();

// delete developer's private functions
let _proto = Object.getPrototypeOf(main);
for (let key in _proto) {
  if (key.charAt(0) === "_") {
    delete _proto[key];
  }
}

window.eleme = main;
